package com.my.object.pool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by ZhongGang
 * at 2018/7/23 10:26
 */
public abstract class AbstractObjectPool<V> implements ObjectPool<V> {
    private int min;//对象池中实例最小个数
    private int max;//对象池中实例最大个数
    private int borrowed;//对象池借出的实例个数
    private List<V> vs = new ArrayList<>();
    private ReentrantLock lock = new ReentrantLock();
    private Condition available = lock.newCondition();

    public AbstractObjectPool(int min, int max) {
        if (min <= 0) {
            throw new IllegalArgumentException("the argument min must be greater than 0");
        }
        if (max <= 0) {
            throw new IllegalArgumentException("the argument max must be greater than 0");
        }
        this.min = min;
        this.max = Math.max(min, max);

        //默认构造时创建实例放到对象池中
        for (int i = 0; i < this.min; i++) {
            V v = create();
            this.vs.add(v);
        }
    }

    @Override
    public V create() {
        throw new UnsupportedOperationException("Not yet implemented!");
    }

    @Override
    public V borrow() {
        try {
            this.lock.lock();
            if (this.borrowed >= this.min && this.borrowed < this.max) {
                V v = create();
                this.vs.add(v);
            }

            while (this.vs.isEmpty()) {
                this.available.awaitUninterruptibly();
            }
            V v = this.vs.get(0);
            this.vs.remove(0);
            this.borrowed++;
            return v;
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public void returnBack(V v) {
        try {
            this.lock.lock();
            this.vs.add(v);
            this.borrowed--;
            this.available.signalAll();
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public void returnBack(V v, ReturnBackRestorer handler) {
        try {
            this.lock.lock();
            handler.restore(v);
            this.returnBack(v);
        } finally {
            this.lock.unlock();
        }
    }
}
